﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using DXBase;
using System.Runtime.InteropServices;

namespace DXBase.Structs
{
	// this struct has the same layout as DXBase.float2
	[StructLayout(LayoutKind.Sequential)]
	public struct Vector2F
	{
		public float X, Y;

		public Vector2F(float x, float y)
		{
			X = x;
			Y = y;
		}

		unsafe public static implicit operator float2(Vector2F v) 
		{
			float2 dst;
			*&dst = *(float2*)&v;
			return dst;
		}
		unsafe public static implicit operator Vector2F(float2 v)
		{
			Vector2F dst;
			*&dst = *(Vector2F*)&v;
			return dst;
		}
		public static implicit operator Vector2F(Windows.Foundation.Point v) { return new Vector2F((float)v.X, (float)v.Y); }
		public static implicit operator Windows.Foundation.Point(Vector2F v) { return new Windows.Foundation.Point(v.X, v.Y); }
		public static implicit operator Vector2F(Windows.Foundation.Size v)  { return new Vector2F((float)v.Width, (float)v.Height); }
		public static implicit operator Windows.Foundation.Size(Vector2F v)  { return new Windows.Foundation.Size(v.X, v.Y); }

		unsafe public float this[int i]
		{
			get
			{
				if (i < 0 || i >= 2)
					throw new ArgumentOutOfRangeException();
				fixed (float* p = &X)
					return p[i];
			}
			set
			{
				if (i < 0 || i >= 2)
					throw new ArgumentOutOfRangeException();
				fixed (float* p = &X)
					p[i] = value;
			}
		}

		public static Vector2F Lerp(Vector2F v1, Vector2F v2, float t)
		{
			return new Vector2F(
				(1 - t) * v1.X + t * v1.X, 
				(1 - t) * v1.Y + t * v2.Y
			);
		}

		public static Vector2F operator +(Vector2F v1, Vector2F v2)
		{
			return new Vector2F(v1.X + v2.X, v1.Y + v2.Y);
		}
		public static Vector2F operator +(Vector2F v)
		{
			return v;
		}
		public static Vector2F operator -(Vector2F v1, Vector2F v2)
		{
			return new Vector2F(v1.X - v2.X, v1.Y - v2.Y);
		}
		public static Vector2F operator -(Vector2F v)
		{
			return new Vector2F(-v.X, -v.Y);
		}
		public static float operator *(Vector2F v1, Vector2F v2)
		{
			return v1.X * v2.X + v1.Y * v2.Y;
		}
		public static Vector2F operator *(float f, Vector2F v)
		{
			return new Vector2F(f * v.X, f * v.Y);
		}
		public static Vector2F operator *(Vector2F v, float f)
		{
			return f * v;
		}
		public static Vector2F operator /(Vector2F v, float f)
		{
			return (1/f) * v;
		}
		public float Cross(Vector2F v)
		{
			return this.X * v.Y - this.Y * v.X;
		}
		public Vector2F Normalize()
		{
			return this / Length;
		}

		public float Length { get { return (this * this).Sqrtf(); } }

		// == convenient shortcuts
		public Vector2F Rotate(float degrees)
		{
			var rad = degrees.DegToRad();
			var cos = rad.Cosf();
			var sin = rad.Sinf();
			return new Vector2F(
				X * cos - Y * sin,
				X * sin + Y * cos
			);
		}
		public Vector2F Scale(float dx, float dy)
		{
			return new Vector2F(X * dx, Y * dy);
		}
		public Vector2F Scale(Vector2F v)
		{
			return Scale(v.X, v.Y);
		}

		public override string ToString()
		{
			return string.Format("({0}, {1})", X, Y);
		}
	}

	// this struct has the same layout as DXBase.float3
	[StructLayout(LayoutKind.Sequential)]
	public struct Vector3F
	{
		public float X, Y, Z;

		//public Vector2F() automatic
		public Vector3F(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}

		unsafe public static implicit operator float3(Vector3F v)
		{
			float3 dst;
			*&dst = *(float3*)&v;
			return dst;
		}
		unsafe public static implicit operator Vector3F(float3 v) 
		{
			Vector3F dst;
			*&dst = *(Vector3F*)&v;
			return dst;
		}

		unsafe public float this[int i]
		{
			get
			{
				if (i < 0 || i >= 3)
					throw new ArgumentOutOfRangeException();
				fixed (float* p = &X)
					return p[i];
			}
			set
			{
				if (i < 0 || i >= 3)
					throw new ArgumentOutOfRangeException();
				fixed (float* p = &X)
					p[i] = value;
			}
		}

		public static Vector3F Lerp(Vector3F v1, Vector3F v2, float t)
		{
			return new Vector3F(
				(1 - t) * v1.X + t * v1.X, 
				(1 - t) * v1.Y + t * v2.Y, 
				(1 - t) * v1.Z + t * v2.Z
			);
		}

		public static Vector3F operator +(Vector3F v1, Vector3F v2)
		{
			return new Vector3F(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
		}
		public static Vector3F operator +(Vector3F v)
		{
			return v;
		}
		public static Vector3F operator -(Vector3F v1, Vector3F v2)
		{
			return new Vector3F(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z);
		}
		public static Vector3F operator -(Vector3F v)
		{
			return new Vector3F(-v.X, -v.Y, -v.Z);
		}
		public static float operator *(Vector3F v1, Vector3F v2)
		{
			return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z;
		}
		public static Vector3F operator *(float f, Vector3F v)
		{
			return new Vector3F(f * v.X, f * v.Y, f * v.Z);
		}
		public static Vector3F operator *(Vector3F v, float f)
		{
			return f * v;
		}
		public static Vector3F operator /(Vector3F v, float f)
		{
			return (1 / f) * v;
		}
		public Vector3F Cross(Vector3F v)
		{
			return new Vector3F(
				this.Y * v.Z - this.Z * v.Y,
				this.Z * v.X - this.X * v.Z,
				this.X * v.Y - this.Y * v.X
				);
		}
		public Vector3F Normalize()
		{
			return this / Length;
		}

		public float Length { get { return (this * this).Sqrtf(); } }

		// == convenient shortcuts
		public Vector3F Rotate(float axisX, float axisY, float axisZ, float degrees)
		{
			return QuaternionF.Rotation(axisX, axisY, axisZ, degrees) * this;
		}
		public Vector3F Rotate(Vector3F axis, float degrees)
		{
			return QuaternionF.Rotation(axis, degrees) * this;
		}
		public Vector3F Scale(float dx, float dy, float dz)
		{
			return new Vector3F(X * dx, Y * dy, Z * dz);
		}
		public Vector3F Scale(Vector3F v)
		{
			return Scale(v.X, v.Y, v.Z);
		}

		public override string ToString()
		{
			return string.Format("({0}, {1}, {2})", X, Y, Z);
		}
	}

	// this struct has the same layout as DXBase.float4
	[StructLayout(LayoutKind.Sequential)]
	public struct Vector4F
	{
		public float X, Y, Z, W;

		//public Vector2F() automatic
		public Vector4F(float x, float y, float z, float w)
		{
			X = x;
			Y = y;
			Z = z;
			W = w;
		}
		public Vector4F(Vector3F v, float w)
		{
			X = v.X;
			Y = v.Y;
			Z = v.Z;
			W = w;
		}

		unsafe public static implicit operator float4(Vector4F v) 
		{
			float4 dst;
			*&dst = *(float4*)&v;
			return dst;
		}
		unsafe public static implicit operator Vector4F(float4 v)
		{
			Vector4F dst;
			*&dst = *(Vector4F*)&v;
			return dst;
		}

		unsafe public float this[int i]
		{
			get
			{
				if (i < 0 || i >= 4)
					throw new ArgumentOutOfRangeException();
				fixed (float* p = &X)
					return p[i];
			}
			set
			{
				if (i < 0 || i >= 4)
					throw new ArgumentOutOfRangeException();
				fixed (float* p = &X)
					p[i] = value;
			}
		}

		public static Vector4F Lerp(Vector4F v1, Vector4F v2, float t)
		{
			return new Vector4F(
				(1 - t) * v1.X + t * v1.X, 
				(1 - t) * v1.Y + t * v2.Y, 
				(1 - t) * v1.Z + t * v2.Z,
				(1 - t) * v1.W + t * v2.W
			);
		}

		public static Vector4F operator +(Vector4F v1, Vector4F v2)
		{
			return new Vector4F(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z, v1.W + v2.W);
		}
		public static Vector4F operator +(Vector4F v)
		{
			return v;
		}
		public static Vector4F operator -(Vector4F v1, Vector4F v2)
		{
			return new Vector4F(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z, v1.W - v2.W);
		}
		public static Vector4F operator -(Vector4F v)
		{
			return new Vector4F(-v.X, -v.Y, -v.Z, -v.W);
		}
		public static float operator *(Vector4F v1, Vector4F v2)
		{
			return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z + v1.W * v2.W;
		}
		public static Vector4F operator *(float f, Vector4F v)
		{
			return new Vector4F(f * v.X, f * v.Y, f * v.Z, f * v.W);
		}
		public static Vector4F operator *(Vector4F v, float f)
		{
			return f * v;
		}
		public static Vector4F operator /(Vector4F v, float f)
		{
			return (1 / f) * v;
		}
		public Vector4F Normalize()
		{
			return this / Length;
		}

		public float Length { get { return (this * this).Sqrtf(); } }

		public override string ToString()
		{
			return string.Format("({0}, {1}, {2}, {3})", X, Y, Z, W);
		}
	}
}
